package com.cz.common.utils;


import org.springframework.boot.system.ApplicationHome;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import sun.misc.BASE64Decoder;

import javax.crypto.Cipher;
import java.io.*;
import java.net.NetworkInterface;
import java.security.KeyStore;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.text.SimpleDateFormat;
import java.util.*;

@RestController
@RequestMapping("/authorize")
public class authorize {
    private static final int MAX_DECRYPT_BLOCK=256;
    @RequestMapping("/checkLicence")
    public R checkLicence(){
        try {
            MD5Code md5c = new MD5Code();
            ApplicationHome h = new ApplicationHome(getClass());
            String dirPath = h.getSource().getParentFile().toString();
            //证书是否存在
            File file = new File(dirPath + "/licence/licence.lic");
            if (!file.exists()) {
                return R.ok().put("result", "2");
            }
            String licence = readFileToString(dirPath + "/licence/licence.lic");
            String decriptliccontent = DecriptWithRSA_Pub(licence, dirPath + "/licence/publicCerts.keystore");
            Map<String,String> infoMap = getInfo(decriptliccontent);
            //验证是否是本机器授权
            String macchineCode = getMachineCode();
            String codeMD5 = md5c.getMD5ofStr("MACHINECODE="+macchineCode + "hp");
            if(!codeMD5.equals(infoMap.get("LICENSESIGN"))){
                return R.ok().put("result", "2");
            }
            //验证授权时间是否到期--先判断是不是永久有效
            if("1".equals(infoMap.get("LICENSETYPE"))){
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                System.out.println(infoMap.get("EXPIREDAY")+" 23:59:59================到期时间");
                long t1=System.currentTimeMillis();
                long t2 = sdf.parse(infoMap.get("EXPIREDAY")+" 23:59:59").getTime();
                if(t2-t1<0){
                    return R.ok().put("result", "1");
                }
            }
            //验证通过返回值
            return R.ok().put("result", "0");
        }catch (Exception e){
            return R.ok().put("result", "2");
        }
    }

    @RequestMapping("/getLicenceCode")
    public R getLicenceCode() throws Exception {
        String macchineCode = getMachineCode();
        return R.ok().put("code", macchineCode);
    }
    @RequestMapping("/uploadLicence")
    public R uploadLicence(@RequestParam("file") MultipartFile licenceFile) {
        try {
            ApplicationHome h = new ApplicationHome(getClass());
            String dirPath = h.getSource().getParentFile().toString();
            File file = new File(dirPath + "/licence/licence.lic");
            if (!file.exists()) {
                try {
                    file.createNewFile();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            FileOutputStream fos = new FileOutputStream(file, false);
            fos.write(licenceFile.getBytes());
            fos.close();
        } catch (Exception e){
            e.printStackTrace();
        }
        return R.ok();
    }


    public String DecriptWithRSA_Pub(String data,String path) throws Exception {
        int len = data.length();
        FileInputStream in = new FileInputStream(path);
        KeyStore ks = KeyStore.getInstance("JKS");// JKS: Java KeyStoreJKS，可以有多种类型
        ks.load(in, "public_password1234".toCharArray());
        in.close();
        String alias = ks.aliases().nextElement();
        X509Certificate x509Certificate = (X509Certificate) ks.getCertificate(alias);
        // 获得公钥
        PublicKey publicKey = x509Certificate.getPublicKey();

        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        SecureRandom random = new SecureRandom();
        BASE64Decoder decoder = new BASE64Decoder();
        byte[] bEncrypt = Base64.getDecoder().decode(data);
        //byte[] bEncrypt = decoder.decodeBuffer(data);
        //公钥解密
        cipher.init(Cipher.DECRYPT_MODE, publicKey, random);
        String decryptData = "";
        int inputLen = bEncrypt.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段解密
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
                cache = cipher.doFinal(bEncrypt, offSet, MAX_DECRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(bEncrypt, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * MAX_DECRYPT_BLOCK;
        }
        byte[] decryptedData = out.toByteArray();
        out.close();
        return new String(decryptedData);
    }
    public String readFileToString(String path) {
        // 定义返回结果
        String jsonString = "";

        BufferedReader in = null;
        try {
            in = new BufferedReader(new InputStreamReader(new FileInputStream(new File(path)), "UTF-8"));// 读取文件
            String thisLine = null;
            while ((thisLine = in.readLine()) != null) {
                jsonString += thisLine;
            }
            in.close();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException el) {
                }
            }
        }

        // 返回拼接好的JSON String
        return jsonString;
    }

    private String getMac() {
        try {
            Enumeration<NetworkInterface> el = NetworkInterface
                    .getNetworkInterfaces();
            while (el.hasMoreElements()) {
                byte[] mac = el.nextElement().getHardwareAddress();
                if (mac == null)
                    continue;
                String hexstr = bytesToHexString(mac);
                return hexstr.toUpperCase();
            }
        } catch (Exception exception) {
            exception.printStackTrace();
        }
        return null;
    }

    public String bytesToHexString(byte[] src){
        StringBuilder stringBuilder = new StringBuilder("");
        if (src == null || src.length <= 0) {
            return null;
        }
        for (int i = 0; i < src.length; i++) {
            int v = src[i] & 0xFF;
            String hv = Integer.toHexString(v);
            if (hv.length() < 2) {
                stringBuilder.append(0);
            }
            stringBuilder.append(hv);
        }
        return stringBuilder.toString();
    }
    public  String getMachineCode() throws Exception{
        Set<String> result = new HashSet<>();
        String mac = getMac();
        result.add(mac);
        Properties props = System.getProperties();
        String javaVersion = props.getProperty("java.version");
        result.add(javaVersion);
        String osVersion = props.getProperty("os.version");
        result.add(osVersion);
        String javaVMVersion = props.getProperty("java.vm.version");
        result.add(javaVMVersion);
        MD5Code md5c = new MD5Code();
        String code = md5c.getMD5ofStr(result.toString());
        return getSplitString(code, "-", 4);

    }
    public String getSplitString(String code,String flag,int length){
        String result = "";
        for(int i=0;i<code.length();i+=length){
            if(i+length<code.length()){
                result += code.substring(i,i+length);
            }else{
                result += code.substring(i);
            }
            result +=flag;
        }
        return result.substring(0,result.length()-1);
    }
    public Map<String,String> getInfo(String str){
        Map<String,String> map = new HashMap<>();
        String[] infos = str.split(",");
        for(String item:infos){
            String[] info = item.split("=");
            map.put(info[0],info[1]);
        }
        return map;
    }

}
